Kerberos has been the de-facto industry standard for Single-Sign-On for many years but not yet been widely adapted for intranet/web-applications. Firefox supports GSSAPI (on Linux/Unix and Windows - using
MIT Kerberos for Windows (KfW)) and SSPI (Windows) for Kerberos authentication quite a while, usable since version 1.5.
Because Microsoft also uses NTLM for SSO purposes they invented a GSSAPI pseudo-mechanism named
SPNEGO to do negotiating of which of the protocols to use - fortunately MIT kerberbos (since version 1.5) supports SPNEGO (thanks to SUN donating a implementation) so we can use MITs GSSAPI library for the server side. SPNEGO is yet only supported on Linux platforms (KfW 3.1 includes a krb library of version 1.4.5 - 3.2 which is currently in beta status will have 1.6+) but at least Mozilla will automatically fall back to plain kerberos authentication which in case of a MIT server side will perfectly work.
Update: KfW 3.2 has been released today - SPNEGO now works on Windows.
I have not yet had a chance to experiment in a SSPI environment and there are rumors about Microsoft not implementing their own protocol (SPNEGO) correctly, so I cannot really say whether it is compatible.
There is a (not yet standardized)
extension to the HTTP/1.1 specification which specifies the "HTTP negotiate" authentication method. We try to use it for SSO on web-applications and web-resources.
I wrote a
PHP extension which gives server side support for this kind of authentication. Continue reading for directions on how to use it...
The Client side
I'm going to describe a setup using Mozilla Firefox (recent version) on either Linux and Windows using MIT Kerberos for Windows as GSSAPI implementation.
There are some firefox settings concerning negotiate authentication:
- network.negotiate-auth.trusted-uris (default: empty) - For which URLs negotiate authentication should be done
- network.negotiate-auth.delegation-uris (default: empty) - For which URLs credential delegation will be allowed - This will in fact give the server side the right to act in your name - WARNING: this requires the client to aquire a forward ticket which is not cached and thus causes a dramatical slow-down of the request.
- network.negotiate-auth.allow-proxies (default: true) - Enables proxy authentication using the negotiate method / The Squid guys want to support it in version 3 - I'm interested in this, too .. watch this blog for further development
- network.negotiate-auth.using-native-gsslib - Use the default GSSAPI library (does not mean SSPI on windows)
- network.negotiate-auth.gsslib (default:empty) - Specifies a alternate GSSAPI shared library
- network.auth.use-sspi (only on Windows, default: true) - Whether to use Microsoft's SSPI library, if disabled use GSSAPI
As said before MIT's KfW does not yet support SPNEGO in the stable branch but Mozilla has implemented a fallback to the Kerberos5 GSSAPI mechanism - this does indeed break the "HTTP Negotiate auth"-specification and will not work for a server side implementation that relies/excepts SPNEGO data although as both are GSSAPI mechanisms they should handled transparently.
Unfortunately Firefox is not quite verbose (read: very silent) concerning errors in the negotiate authentication process. You best friend when trying to do negotiate authentication is settung the environment variable
NSPR_LOG_MODULES=negotiateauth:5 and starting Firefox with the -console option.
Internet Exporer
I have not yet tested IE in a kerberos enabled environment but if there is no kerberos available it will fall back to plain NTLMSSP - abusing the Negotiate authentication scheme. As this mechanism is not supported on the server side the authentication will fail with no possiblity of a fallback. There are two possible work-arounds: You can disable "Integrated Windows Authentication" in the preferences (needs user interaction and wont be possible if you are using NTLM somewhere else) or check for the user agent on server side and simply don't send a negotiate header in case of a MS browser.
The Server side
There are some implementations of negotiate authentication for the apache webserver, mainly mod_auth_kerb and mod_auth_gss but I wanted to have in-application support (I also want to use webservers other than apache) for this authentication scheme so I decided to integrate support for it in my Kerberos extension for PHP - this also provides support for credential delegation what might be really useful in some cases.
CGI servers
This can only work when the webserver passes the authorization data down to PHP what is disabled on Apache for CGIs but there is a simple workaround utilizing mod_rewrite:
RewriteEngine on
RewriteBase /
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*)$ $1 [e=HTTP_AUTHORIZATION:%1]
This will pass the authorization header to the CGI.
Usage Example
<?php
die('KRB5 Extension not installed'); }
// The browser has provided authorization data
if(!empty($_SERVER['HTTP_AUTHORIZATION'])) {
list($mech, $data) = split(' ', $_SERVER['HTTP_AUTHORIZATION']);
// you should check the credentials supplied by basic authentication here
// e.g. try to obtain a ticket (which is possible using the extension,too) using them
// to authenticate against a kerberos realm
die('Unsupported request'); }
// do the negotiate authentication
// '/http.ktab' must contain a key for principal 'HTTP@F.Q.D.N'
$auth = new KRB5NegotiateAuth('/http.ktab');
$reply = '';
// successful authentication of user
header('HTTP/1.1 200 Success'); // send additional data
echo 'Success - authenticated as ' . $auth->getAuthenticatedUser();
} else {
// there is some error (including e.g. an expired ticket)
header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic');
}
} else {
// Send a authentication request, we send
// both Negotiate and Basic (as fallback)
header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Negotiate'); header('WWW-Authenticate: Basic', false);
echo 'Not authenticated'; }
?>
Please note that this is completely stateless and relies on the first token sent by the browser to be enough for context establishment. I have not yet seen any cases (I think it is required due to the support for NTLM which we don't have here) where this wasn't sufficient but according to the specification it is not correct.
I'll also probably change the interface to the class, using exceptions and doing more of the header/data handling internally.
Security considerations
Mutual authentication is difficult to implement and as far as I can see Mozilla generally makes no use of it, meaning that the server identity cannot be trusted. This scheme also does not provide integrity and confidentiality mechanisms so using SSL is a must for untrusted environments.
If you are not using SSL the ticket data can also be sniffed by a third party - this data is quite safe but can be reused in a replay attack within a timespan of about 5 minutes, enabling unauthorized attackers to authenticate as a valid user.
Whats next?
In my opionion it is quite interesting to see how Microsofts implementations behave, but have not yet manage to set up a testing environment (AD,IIS,ISA), I'll keep you updated.
I also work on a GSS/SPNEGO squid authentication helper (there already is one using SSPI) (yeah, no more authentication popups
) which works but requires further testing and some clarification by the squid people about the helper protocol used.